<?php
class ImageModel extends Azalea\Model
{
  private $_imageinfo;
  private $_preset;
  private $_processIm;

  private function _scale($im, $width, $height, $out = true)
  {
    $aspect = $this->_imageinfo[1] / $this->_imageinfo[0];
    if ($aspect < $height / $width) {
      if ($out) {
        $twidth = intval($height / $aspect);
        $theight = $height;
      } else {
        $height = intval($width * $aspect);
      }
    } else {
      if ($out) {
        $twidth = $width;
        $theight = intval($width * $aspect);
      } else {
        $width = intval($height / $aspect);
      }
    }
    if ($out) {
      $this->_resize($im, $twidth, $theight);
      $cloneIm = imagecreatetruecolor($twidth, $theight);
      imagefill($cloneIm, 0, 0, imagecolorallocate($cloneIm, 255, 255, 255));
      imagecopy($cloneIm, $this->_processIm, 0, 0, 0, 0, $twidth, $theight);
      imagedestroy($this->_processIm);
      $this->_processIm = null;
      $this->_imageinfo[0] = $twidth;
      $this->_imageinfo[1] = $theight;
      $this->_crop($cloneIm, $width, $height);
    } else {
      $this->_resize($im, $width, $height);
    }
  }

  private function _crop($im, $width, $height)
  {
    if ($this->_imageinfo[0] > $width) {
      $x = ($this->_imageinfo[0] - $width) / 2;
    } else {
      $x = 0;
      if ($this->_imageinfo[0] < $width) {
        $width = $this->_imageinfo[0];
      }
    }
    if ($this->_imageinfo[1] > $height) {
      $y = ($this->_imageinfo[1] - $height) / 2;
    } else {
      $y = 0;
      if ($this->_imageinfo[1] < $height) {
        $height = $this->_imageinfo[1];
      }
    }
    if (!isset($this->_processIm)) {
      $this->_processIm = imagecreatetruecolor($width, $height);
      imagefill($this->_processIm, 0, 0, imagecolorallocate($this->_processIm, 255, 255, 255));
    }
    imagecopy($this->_processIm, $im, 0, 0, $x, $y, $width, $height);
  }

  private function _resize($im, $width, $height)
  {
    if (!isset($this->_processIm)) {
      $this->_processIm = imagecreatetruecolor($width, $height);
      imagefill($this->_processIm, 0, 0, imagecolorallocate($this->_processIm, 255, 255, 255));
    }
    imagecopyresampled($this->_processIm, $im, 0, 0, 0, 0, $width, $height, $this->_imageinfo[0], $this->_imageinfo[1]);
  }

  private function _grayscale()
  {
    imagefilter($this->_processIm, IMG_FILTER_GRAYSCALE);
  }

  public function process($source, $preset)
  {
    if (!isset($preset['width']) || !isset($preset['height'])) {
      // size not set
      return false;
    }
    $imageinfo = getimagesize($source);
    if ($imageinfo[2] == IMAGETYPE_GIF) {
      $im = imagecreatefromgif($source);
    } else if ($imageinfo[2] == IMAGETYPE_JPEG) {
      $im = imagecreatefromjpeg($source);
    } else if ($imageinfo[2] == IMAGETYPE_PNG) {
      $im = imagecreatefrompng($source);
    } else {
      // format is invalid
      return false;
    }
    if (function_exists('exif_read_data')) {
      $exif = exif_read_data($source);
      if(!empty($exif['Orientation'])) {
        switch($exif['Orientation']) {
          case 3:
            $im = imagerotate($im, 180, 0);
            break;
          case 6:
            $im = imagerotate($im, -90, 0);
            $t = $imageinfo[0];
            $imageinfo[0] = $imageinfo[1];
            $imageinfo[1] = $t;
            break;
          case 8:
            $im = imagerotate($im, 90, 0);
            $t = $imageinfo[0];
            $imageinfo[0] = $imageinfo[1];
            $imageinfo[1] = $t;
            break;
        }
      }
    }
    $this->_imageinfo = $imageinfo;
    $preset['type'] = (isset($preset['type']) && in_array(strtolower($preset['type']), array('crop', 'resize', 'scale'))) ?
        strtolower($preset['type']) : 'scale';
    $this->_preset = $preset;
    switch ($preset['type']) {
      case 'crop':
        $this->_crop($im, $preset['width'], $preset['height']);
        break;
      case 'resize':
        $this->_resize($im, $preset['width'], $preset['height']);
        break;
      case 'scale':
        $this->_scale($im, $preset['width'], $preset['height'], !isset($preset['out']) || $preset['out']);
        break;
    }
    if (isset($preset['grayscale'])) {
      $this->_grayscale();
    }
    imagedestroy($im);
  }

  public function output($destination = null)
  {
    if (!isset($this->_imageinfo) || !isset($this->_processIm)) {
      return;
    }
    $save = is_string($destination) && !empty($destination);
    if (!$save) {
      header('Content-type: ' . $this->_imageinfo['mime']);
    }
    switch ($this->_imageinfo[2]) {
      case IMAGETYPE_GIF:
        if ($save) {
          imagegif($this->_processIm, $destination);
        } else {
          imagegif($this->_processIm);
        }
        break;
      case IMAGETYPE_JPEG:
        $quality = isset($this->_preset['quality']) ? intval($this->_preset['quality']) : 80;
        if ($save) {
          imagejpeg($this->_processIm, $destination, $quality);
        } else {
          imagejpeg($this->_processIm, null, $quality);
        }
        break;
      case IMAGETYPE_PNG:
        if ($save) {
          imagepng($this->_processIm, $destination);
        } else {
          imagepng($this->_processIm);
        }
        break;
    }
    $this->close();
  }

  public function close()
  {
    if (isset($this->_processIm)) {
      imagedestroy($this->_processIm);
      $this->_processIm = null;
    }
  }
}
